package com.woniu.smart.security;

import cn.hutool.core.util.StrUtil;
import com.woniu.smart.util.JwtUtils;
import com.woniu.smart.util.RedisCache;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Description TODO
 * @Para a
 * @Retun a
 * @Author WangMr
 * @Date Create by 2022/10/26 16:07
 */
@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private RedisCache redisCache;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String token = request.getHeader("Authorization");
        //如果请求头中得到的token为空，则表示该用户没有登录过，则直接放行，（用户第一次登录请求时就是这情况）
        if(StrUtil.isEmptyOrUndefined(token)){
            chain.doFilter(request,response);
            //不能丢掉return,因为请求前后都要走过滤器，如果下面不加return，则返回后代码还会继续走下面的代码
            return;
        }
        //得到token对应的声明信息
        Claims claims = jwtUtils.getClaimsByToken(token);
        if (claims == null) {
            throw new JwtException("token异常");
        }
        //判断token是否过期
        if(jwtUtils.isTokenExpired(claims)){
            log.info("token已过期");
            throw new JwtException("token已过期");
        }
        //得到账号信息
        String username = claims.getSubject();
        //根据账号信息从redis中取出当前用户、权限等信息
        LoginUser loginUser = (LoginUser) redisCache.getCacheObject("rbac" + username);
        // 生成authenticationToken，注意此处用的是三个参数的UsernamePasswordAuthenticationToken，点击进入可以看到三个参数默认是谁成功
        // 参数: 用户名 密码 权限信息
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        //后续security就能获取到当前登录的用户信息了，也就完成了用户认证。
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        //继续调用下面的过滤器
        chain.doFilter(request,response);

    }
}
